#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "lcd_driver.h"
#include "NesCPU6502.h"
#include "NesSystem.H"
#include "NesPPU.h"


static inline void vNesPPUMemorySet(uint16_t addr, uint8_t value);
static inline uint8_t ucNesPPUMemoryGet(uint16_t addr);


// From http://nesdev.com/pal.txt
/* RGB565 */
const uint16_t cn_uxPaletteColorRGB[64] = {
0xAE73, 0xD120, 0x1500, 0x1340, 0x0E88, 0x02A8, 0x00A0, 0x4078, 0x6041, 0x2002, 0x8002, 0xE201, 0xEB19, 0x0000, 0x0000, 0x0000,
0xF7BD, 0x9D03, 0xDD21, 0x1E80, 0x17B8, 0x0BE0, 0x40D9, 0x61CA, 0x808B, 0xA004, 0x4005, 0x8704, 0x1104, 0x0000, 0x0000, 0x0000,
0xFFFF, 0xFF3D, 0xBF5C, 0x5FA4, 0xDFF3, 0xB6FB, 0xACFB, 0xC7FC, 0xE7F5, 0x8286, 0xE94E, 0xD35F, 0x5B07, 0x0000, 0x0000, 0x0000,
0xFFFF, 0x3FAF, 0xBFC6, 0x5FD6, 0x3FFE, 0x3BFE, 0xF6FD, 0xD5FE, 0x34FF, 0xF4E7, 0x97AF, 0xF9B7, 0xFE9F, 0x0000, 0x0000, 0x0000,
};


/* PPU CHR 存储区域 */
uint8_t st_ucPPUCHRRAM[0x2000] = {0};
/* OAM RAM 存储64个精灵数据 */
uint8_t st_ucPPUOAMRam[256] = {0};
/* Name Tables 1k * 4 名称表 */
uint8_t st_ucPPUNameTables[0x400 * 4];
/* OAM RAM 当前读写地址 */
uint8_t st_ucPPURamOAMAddr = 0;
/* PPU RAM 当前读写地址 */
uint16_t st_usPPURamAddr = 0;
/* 寄存器地址高低位读取与写入标志 */
static uint8_t st_ucPPURamAddrCnt = 0;

NesPPUInofType st_typeNesPPUInfo = {0};


int8_t cNesPPUBankAddr4kByteSet(int8_t cSerial, uint32_t uiAddr)
{
    uint8_t *pucAddr = g_typeNesInfo.CHR_ROM + (uiAddr % g_typeNesInfo.CHR_ROMSize);

    if(g_typeNesInfo.CHR_ROMSize == 0)
        return 1;

    if(cSerial == 0)
        st_typeNesPPUInfo.PatternTablesBank0 = pucAddr;
    else
        st_typeNesPPUInfo.PatternTablesBank1 = pucAddr;

    return 0;
}

int8_t cNesPPUBankAddr8kByteSet(uint32_t uiAddr)
{
    int8_t cError;

    cError  = cNesPPUBankAddr4kByteSet(0, uiAddr);
    cError |= cNesPPUBankAddr4kByteSet(1, uiAddr + 1024 * 4);

    return cError;
}

void vNesPPUMemoryLoad(int8_t cSerial, uint32_t uiAddr, int32_t iLength)
{
    uint8_t *pucAddr = g_typeNesInfo.CHR_ROM + (uiAddr % g_typeNesInfo.CHR_ROMSize);
    uint8_t *pucCHRRAMBase = (uint8_t *)st_ucPPUCHRRAM;

    if(g_typeNesInfo.CHR_ROMSize == 0)
        return;

    /* 分成8块，每块1kBtye */
    if(((1024 * cSerial) + iLength) > 0x2000)
        return;

    /* 之所以用内存拷贝，是为了方便PPU代码画点时寻址，并且有些Mapper也需要把此图案区域当成可写入的状态 */
    memcpy(pucCHRRAMBase + 1024 * cSerial, pucAddr, iLength);
}

void vNesPPUOamRamSet(uint8_t ucAddr, uint8_t *pucBuff, int16_t iLength)
{
    uint8_t *pucHandle = ((uint8_t *)st_ucPPUOAMRam) + ucAddr;

    memcpy(pucHandle, pucBuff, iLength);
}

static inline uint8_t ucNesPPUMemoryGet(uint16_t addr)
{
    uint8_t value;

    /* 16k地址，后面进行镜像 */
    addr &= 0x3FFF;
    if(addr < 0x1000)
    {
        value = st_typeNesPPUInfo.PatternTablesBank0[addr];
    }
    else if(addr < 0x2000)
    {
        value = st_typeNesPPUInfo.PatternTablesBank1[addr & 0x0FFF];
    }
    else if(addr < 0x3F00)
    {
        /* 4k个Byte，后面进行镜像 */
        value = st_typeNesPPUInfo.NameTables[addr & 0x0FFF];
    }
    else
    {
        /* 32个Byte，后面进行镜像 */
        value = (addr & 0x10) ? st_typeNesPPUInfo.SpritePalettes[addr & 0x0F] : st_typeNesPPUInfo.BackgroundPalettes[addr & 0x0F];
    }

    return value;
}

static inline void vNesPPUMemorySet(uint16_t addr, uint8_t value)
{
    /* 16k地址，后面进行镜像 */
    addr &= 0x3FFF;
    if(addr < 0x1000)
    {
        st_typeNesPPUInfo.PatternTablesBank0[addr] = value;
    }
    else if(addr < 0x2000)
    {
        st_typeNesPPUInfo.PatternTablesBank1[addr & 0x0FFF] = value;
    }
    else if(addr < 0x3F00)
    {
        /* 4k个Byte，后面进行镜像 */
        addr &= 0xFFF;

        /* 水平镜像（左右） */
        if(g_typeNesInfo.mirrorType == MIRROR_TYPE_HORIZONTAL)
        {
            st_typeNesPPUInfo.NameTables[addr | 0x400] = value;
            st_typeNesPPUInfo.NameTables[addr & ~0x400] = value;
        }
        /* 垂直镜像（上下） */
        else if(g_typeNesInfo.mirrorType == MIRROR_TYPE_VERTICAL)
        {
            st_typeNesPPUInfo.NameTables[addr | 0x800] = value;
            st_typeNesPPUInfo.NameTables[addr & ~0x800] = value;
        }
        /* single镜像 */
        else if(g_typeNesInfo.mirrorType < 4)
        {
            addr &= ~0xC00;
            st_typeNesPPUInfo.NameTables[addr] = value;
            st_typeNesPPUInfo.NameTables[addr | 0x400] = value;
            st_typeNesPPUInfo.NameTables[addr | 0x800] = value;
            st_typeNesPPUInfo.NameTables[addr | 0xC00] = value;
        }
        else
        {
            st_typeNesPPUInfo.NameTables[addr] = value;
        }
    }
    else
    {
        /* 32个Byte，后面进行镜像 */
        if((addr & 0x03) == 0)
        {
            st_typeNesPPUInfo.SpritePalettes[addr & 0x0F] = st_typeNesPPUInfo.BackgroundPalettes[addr & 0x0F] = value & 0x3F;

            st_typeNesPPUInfo.BackgroundPalettes[0x04] = st_typeNesPPUInfo.SpritePalettes[0x04] = st_typeNesPPUInfo.BackgroundPalettes[0x00];
            st_typeNesPPUInfo.BackgroundPalettes[0x08] = st_typeNesPPUInfo.SpritePalettes[0x08] = st_typeNesPPUInfo.BackgroundPalettes[0x00];
            st_typeNesPPUInfo.BackgroundPalettes[0x0C] = st_typeNesPPUInfo.SpritePalettes[0x0C] = st_typeNesPPUInfo.BackgroundPalettes[0x00];
        }
        else if(addr & 0x10)
            st_typeNesPPUInfo.SpritePalettes[addr & 0x0F] = value & 0x3F;
        else
            st_typeNesPPUInfo.BackgroundPalettes[addr & 0x0F] = value & 0x3F;
    }
}

uint8_t ucNesPPURegisterGet(uint16_t addr)
{
    static uint8_t st_ucValueLast = 0;
    uint8_t value = 0;

    /* 8个Byte，后面进行镜像 */
    switch(addr & 7)
    {
        case 2:
            value = st_typeNesPPUInfo.registers.PPUSTATUS;
            /* 被读取1次后，需要自动清除中断请求位 */
            st_typeNesPPUInfo.registers.PPUSTATUS &= ~0x80;
            st_ucPPURamAddrCnt = 0;
            break;

        case 4: value = st_ucPPUOAMRam[st_ucPPURamOAMAddr++]; break;

        case 7:
            value = st_ucValueLast;

            /* 读取数据后，地址增加1或者增加32 */
            st_ucValueLast = (st_usPPURamAddr < 0x3F00) ? ucNesPPUMemoryGet(st_usPPURamAddr) : st_ucValueLast;
            st_usPPURamAddr += (st_typeNesPPUInfo.registers.PPUCTRL & 0x04) ? 32 : 1;
            break;

        default : break;
    }

    return value;
}

void vNesPPURegisterSet(uint16_t addr, uint8_t value)
{
    /* 后面nByte是前面8Byte的镜像 */
    switch(addr & 7)
    {
        case 0:
            st_typeNesPPUInfo.registers.PPUCTRL = value;

            st_typeNesPPUInfo.PPURamAddrIncrement = (value & 0x04) ? 32 : 1;
            /* 内部寄存器T/V.nametable */
            st_typeNesPPUInfo.t.nametable = value & 0x03;
            break;
        case 1: st_typeNesPPUInfo.registers.PPUMASK = value; break;
        case 3: st_ucPPURamOAMAddr = value; break;
        case 4: st_ucPPUOAMRam[st_ucPPURamOAMAddr++] = value; break;
        case 5:
            /* X scroll: 第1次为X轴偏移，第2次为Y轴偏移 */
            if((st_ucPPURamAddrCnt & 1) == 0)
                st_typeNesPPUInfo.t.fineX = value;
            else
                st_typeNesPPUInfo.t.fineY = value;

            ++st_ucPPURamAddrCnt;
            break;

            /* PPU 内存地址: 第1次写入地址高字节，第2次写入地址低字节 */
        case 6: st_usPPURamAddr = (st_ucPPURamAddrCnt & 1) ? (st_usPPURamAddr & 0xFF00) | value : (st_usPPURamAddr & 0x00FF) | ((uint16_t)(value & 0x3F) << 8);

            /* Y scroll: 第1次为Y轴偏移，第2次为X轴偏移 */
            if((st_ucPPURamAddrCnt & 1) == 0)
            {
                st_typeNesPPUInfo.t.nametable = (value >> 2) & 0x03;

                st_typeNesPPUInfo.t.fineY &= 0xF8;
                st_typeNesPPUInfo.t.fineY |= (value >> 4) & 0x03;

                st_typeNesPPUInfo.t.fineY &= 0x3F;
                st_typeNesPPUInfo.t.fineY |= (value & 0x03) << 6;
            }
            else
            {
                st_typeNesPPUInfo.t.fineX &= 0x07;
                st_typeNesPPUInfo.t.fineX |= (value & 0x1F) << 3;

                st_typeNesPPUInfo.t.fineY &= 0x38;
                st_typeNesPPUInfo.t.fineY |= (value & 0xE0) >> 2;

                // st_typeNesPPUInfo.v = st_typeNesPPUInfo.t;
            }

            ++st_ucPPURamAddrCnt;
            break;

        case 7:
            /* 地址增加1或者增加32 */
            vNesPPUMemorySet(st_usPPURamAddr, value);
            st_usPPURamAddr += st_typeNesPPUInfo.PPURamAddrIncrement;
            break;

        default : break;
    }
}

void vNesPPUStart(void)
{
    memset((uint8_t *)st_ucPPUOAMRam, 0xFF, sizeof(st_ucPPUOAMRam));
    memset((uint8_t *)st_ucPPUNameTables, 0xFF, sizeof(st_ucPPUNameTables));

    memset(&st_typeNesPPUInfo.t, 0, sizeof(st_typeNesPPUInfo.t));
    memset(&st_typeNesPPUInfo.v, 0, sizeof(st_typeNesPPUInfo.v));
    st_typeNesPPUInfo.PatternTablesBank0 = (uint8_t *)&st_ucPPUCHRRAM[0];
    st_typeNesPPUInfo.PatternTablesBank1 = (uint8_t *)&st_ucPPUCHRRAM[0x1000];
    st_typeNesPPUInfo.NameTables         = (uint8_t *)&st_ucPPUNameTables[0];

    st_typeNesPPUInfo.pointNowX = 0;
    st_typeNesPPUInfo.pointNowY = 0;
    st_typeNesPPUInfo.clockCnt = 0;
    st_typeNesPPUInfo.frameCnt = 0;
}

void vNesPPUInit(void)
{
    memset(&st_typeNesPPUInfo, 0, sizeof(st_typeNesPPUInfo));
}

/* 扫描一行 背景 */
void vNesPPUDrawBackgroundLine(int32_t iPointYNow)
{
    uint8_t *pucNameTableAddrBase, *pucPatternTableAddrBase;
    uint8_t *pucPatternByteLow, *pucPatternByteHigh;
    int32_t iPointNow = iPointYNow << 8;
    int16_t cPointXmask, sFineXNow, sFineXEnd, sTileAddr;
    uint8_t ucNameTableByte, ucNameAttributeBlock;
    uint8_t ucColorBlock, ucColorLow, ucColorHigh, ucColorPoint, ucColorLowY = st_typeNesPPUInfo.v.fineY & 7;
    uint8_t ucPatternByteLow, ucPatternByteHigh;

    st_typeNesPPUInfo.v.nametable = st_typeNesPPUInfo.t.nametable;
    st_typeNesPPUInfo.v.nametable += (st_typeNesPPUInfo.v.fineY >= 240) ? 2 : 0;

    /* 偏移到图案表的起始地址 */
    pucPatternTableAddrBase = ((st_typeNesPPUInfo.registers.PPUCTRL >> 4) & 1) ? st_typeNesPPUInfo.PatternTablesBank1 : st_typeNesPPUInfo.PatternTablesBank0;

    /* 刷新一行256个点 */
    sFineXNow = st_typeNesPPUInfo.t.fineX;
    sFineXEnd = sFineXNow + 256;

    /* 显示背景 */
    while(sFineXNow < sFineXEnd)
    {
        st_typeNesPPUInfo.v.coarseX = (sFineXNow >> 3) & 31;

        st_typeNesPPUInfo.v.nametable += (sFineXNow == 256) ? 1 : 0;
        st_typeNesPPUInfo.v.nametable &= 0x03;

        /* 计算当前Tile地址 */
        sTileAddr = (st_typeNesPPUInfo.v.nametable << 10) + (st_typeNesPPUInfo.v.coarseY << 5) + st_typeNesPPUInfo.v.coarseX;

        /* 读取名称表内对应地址的值 */
        ucNameTableByte = st_typeNesPPUInfo.NameTables[sTileAddr & 4095];

        /* 偏移到图案表内当前图块的起始地址 */
        pucPatternByteLow = &pucPatternTableAddrBase[ucNameTableByte << 4];
        /* 地址偏移到当前tile的图案模型高低起始地址 */
        pucPatternByteHigh = pucPatternByteLow + 8;

        /* 地址偏移到当前tile的图案模型行起始地址 */
        pucPatternByteLow = &pucPatternByteLow[ucColorLowY];
        pucPatternByteHigh = &pucPatternByteHigh[ucColorLowY];


        /* 偏移到名称表的起始地址 */
        pucNameTableAddrBase = &st_typeNesPPUInfo.NameTables[st_typeNesPPUInfo.v.nametable << 10];

        /* 获取Block颜色序号 */
        ucNameAttributeBlock = ((st_typeNesPPUInfo.v.coarseY >> 2) << 3) + (st_typeNesPPUInfo.v.coarseX >> 2);
        ucColorBlock = *(pucNameTableAddrBase + 960 + ucNameAttributeBlock);
        /* 高两位颜色序号 */
        ucColorHigh = (ucColorBlock >> ((((st_typeNesPPUInfo.v.coarseX & 3) >> 1) + (st_typeNesPPUInfo.v.coarseY & 2)) << 1)) & 3;
        ucColorHigh <<= 2;


        /* 本个tile需要渲染x个point */
        cPointXmask = sFineXNow & 7;
        iPointNow += 8 - cPointXmask;

        ucPatternByteLow = *pucPatternByteLow >> cPointXmask;
        ucPatternByteHigh = *pucPatternByteHigh >> cPointXmask;

        /* 1次循环渲染1个tile */
        while((cPointXmask++) < 8)
        {
            /* 低两位颜色序号 */
            ucColorLow = ((ucPatternByteHigh & 1) << 1) + (ucPatternByteLow & 1);

            /* 对应调色板中的颜色值 */
            ucColorPoint = st_typeNesPPUInfo.BackgroundPalettes[ucColorHigh + ucColorLow];

            /* 打点显示 */
            // iPointNow = sPointYNow * PPU_X_MAX_PIXEL + sPointXNow
            LCD_VRAM_SET(iPointNow - cPointXmask, cn_uxPaletteColorRGB[ucColorPoint]);

            ucPatternByteLow >>= 1;
            ucPatternByteHigh >>= 1;
            ++sFineXNow;
        }
    }

    ++st_typeNesPPUInfo.v.fineY;
    st_typeNesPPUInfo.v.coarseY = st_typeNesPPUInfo.v.fineY >> 3;
    st_typeNesPPUInfo.v.coarseY = (st_typeNesPPUInfo.v.coarseY >= 30) ? (st_typeNesPPUInfo.v.coarseY - 30) : st_typeNesPPUInfo.v.coarseY;
}

/* 扫描一行 精灵 */
void vNesPPUDrawSpiritLine(int32_t iPointYNow, NesPPUSpiritInfoType *ptypeSpiritData)
{
    NesPPUSpiritInfoType *ptypeSpiritNow;
    uint8_t *pucSpiritPatternByteLow, *pucSpiritPatternByteHigh;
    int32_t iPointNow;
    uint16_t usBackgroundColorPoint;
    uint8_t ucSpiritColorLow, ucSpiritColorHigh, ucSpiritColorPoint, ucMask;
    int8_t cSpiritPointXmask, cSpiritPointYmask, cSpiritPointXFlip, cSpiritPointYFlip, i;

    /* 显示当前行的8个精灵，0号精灵最后绘制，以显示在最上面 */
    for(ptypeSpiritNow = ptypeSpiritData + 8; ptypeSpiritNow >= ptypeSpiritData; --ptypeSpiritNow)
    {
        /* Y轴大于等于239，表示当前精灵不需要显示 */
        if(ptypeSpiritNow->coordY >= 239)
            continue;

        /* 精灵存储的Y地址+1 为实际地址Y */
        ptypeSpiritNow->coordY += 1;

        /* 兼容 8 * 8 模式与 8 * 16 模式 */
        cSpiritPointYmask = (iPointYNow - ptypeSpiritNow->coordY) & 7;
        cSpiritPointYFlip = (ptypeSpiritNow->attribute & 0x80) ? (7 - cSpiritPointYmask) : cSpiritPointYmask;

        /* 8 * 16 模式 */
        if(st_typeNesPPUInfo.registers.PPUCTRL & 0x20)
        {
            /* title索引号，类似名称表 */
            pucSpiritPatternByteLow  = (ptypeSpiritNow->titleNumber & 1) ? st_typeNesPPUInfo.PatternTablesBank1 : st_typeNesPPUInfo.PatternTablesBank0;
            pucSpiritPatternByteLow += (ptypeSpiritNow->titleNumber & 0xFE) << 4;

            if (ptypeSpiritNow->attribute & 0x80)
            {
                /* 垂直翻转，精灵的上半部分需要再偏移16个Byte */
                pucSpiritPatternByteLow += ((iPointYNow - ptypeSpiritNow->coordY) < 8) ? 16 : 0;
            }
            else
            {
                /* 非垂直翻转，精灵的下半部分需要再偏移16个Byte */
                pucSpiritPatternByteLow += ((iPointYNow - ptypeSpiritNow->coordY) >= 8) ? 16 : 0;
            }
        }
        /* 8 * 8 模式 */
        else
        {
            pucSpiritPatternByteLow  = ((st_typeNesPPUInfo.registers.PPUCTRL >> 3) & 1) ? st_typeNesPPUInfo.PatternTablesBank1 : st_typeNesPPUInfo.PatternTablesBank0;
            pucSpiritPatternByteLow += ptypeSpiritNow->titleNumber << 4;
        }

        /* 偏移到图案表内当前图块的起始地址 */
        pucSpiritPatternByteHigh = pucSpiritPatternByteLow + 8;

        /* 地址偏移到当前tile的图案模型行起始地址 */
        pucSpiritPatternByteLow = &pucSpiritPatternByteLow[cSpiritPointYFlip];
        pucSpiritPatternByteHigh = &pucSpiritPatternByteHigh[cSpiritPointYFlip];

        /* 获取精灵颜色高两位 */
        ucSpiritColorHigh = (ptypeSpiritNow->attribute & 0x03) << 2;

        iPointNow = (iPointYNow << 8) + ptypeSpiritNow->coordX;

        /* tile 列、是否水平翻转 */
        for(ucMask = (ptypeSpiritNow->attribute & 0x40) ? 0x01 : 0x80; ucMask; ucMask = (ptypeSpiritNow->attribute & 0x40) ? (ucMask << 1) : (ucMask >> 1), ++iPointNow)
        {
            /* 获取颜色偏移的低两位 */
            ucSpiritColorLow = (((*pucSpiritPatternByteHigh & ucMask) != 0) << 1) | (((*pucSpiritPatternByteLow & ucMask) != 0) & 1);

            /* 精灵透明像素（0号像素），不覆盖背景 */
            if(ucSpiritColorLow == 0)
                continue;

            /* 获取颜色偏移 */
            ucSpiritColorPoint = st_typeNesPPUInfo.SpritePalettes[ucSpiritColorHigh + ucSpiritColorLow];

            /* 当前扫描线的正常8个精灵渲染 */
            if(ptypeSpiritNow < (ptypeSpiritData + 8))
            {
                /* 0：前台；1：后台，或者背景像素就是0 */
                // iPointNow = sPointYNow * PPU_X_MAX_PIXEL + sPointXNow
                if(((ptypeSpiritNow->attribute & 0x20) == 0) || (LCD_VRAM_GET(iPointNow) == cn_uxPaletteColorRGB[st_typeNesPPUInfo.BackgroundPalettes[0]]))
                {
                    // iPointNow = sPointYNow * PPU_X_MAX_PIXEL + sPointXNow
                    LCD_VRAM_SET(iPointNow, cn_uxPaletteColorRGB[ucSpiritColorPoint]);
                }
            }
            /* 数组内第8个位置的精灵像素特意用来作精灵0击中检测 */
            else
            {
                /* 获取屏幕上当前已经打印的背景点的颜色值 */
                // iPointNow = sPointYNow * PPU_X_MAX_PIXEL + sPointXNow
                usBackgroundColorPoint = LCD_VRAM_GET(iPointNow);
                for(i = 1; i < 16; ++i)
                {
                    if((i & 3) && (usBackgroundColorPoint == cn_uxPaletteColorRGB[st_typeNesPPUInfo.BackgroundPalettes[i]]))
                    {
                        /* 精灵0击中 */
                        st_typeNesPPUInfo.registers.PPUSTATUS |= 0x40;
                        break;
                    }
                }
            }
        }
    }
}

/* 扫描一行 背景 + 精灵 */
uint8_t cNesPPULineScanf(void)
{
    /* 8个精灵的缓存 */
    NesPPUSpiritInfoType typeSpiritData[9];
    NesPPUSpiritInfoType *ptypeSpiritNow, *ptypeSpiritTemp = &typeSpiritData[0];
    int8_t i, j, cPointYDeltaMax, cSpiritZero = 0;


    /* 显示当前 pointNowY 行背景 */
    vNesPPUDrawBackgroundLine(st_typeNesPPUInfo.pointNowY);


    /* 精灵缓存先全部恢复为0xFF */
    memset(typeSpiritData, 0xFF, sizeof(typeSpiritData));

    /* 8 * 8 精灵模式与 8 * 16 精灵模式 */
    cPointYDeltaMax = ((st_typeNesPPUInfo.registers.PPUCTRL & 0x20) == 0) ? 8 : 16;

    /* 遍历出当前行的最多8个精灵，多增加的第9个给溢出检测使用 */
    for(i = 0, j = 0; ((i < 64) && (j < 9)); ++i)
    {
        ptypeSpiritNow = (NesPPUSpiritInfoType *)&st_ucPPUOAMRam[i * 4];
        if((((int16_t)ptypeSpiritNow->coordY + 1) <= st_typeNesPPUInfo.pointNowY) && (((int16_t)ptypeSpiritNow->coordY + 1 + cPointYDeltaMax) > st_typeNesPPUInfo.pointNowY))
        {
            *ptypeSpiritTemp++ = *ptypeSpiritNow;
            ++j;

            /* 是否存在精灵0 */
            cSpiritZero = (i == 0) ? 1 : cSpiritZero;
        }
    }

    /* 精灵溢出标志 */
    st_typeNesPPUInfo.registers.PPUSTATUS |= (j >= 9) ? 0x20 : 0;

    /* 当前帧没有精灵0击中，则进行精灵0检查 */
    if((cSpiritZero != 0) && ((st_typeNesPPUInfo.registers.PPUSTATUS & 0x40) == 0))
    {
        /* 在第8个精灵位置放置精灵0，以在精灵扫描函数中检测精灵0击中 */
        memcpy(&typeSpiritData[8], &typeSpiritData, sizeof(NesPPUSpiritInfoType));
    }
    else
    {
        /* 第8个精灵缓存恢复为0xFF */
        memset(&typeSpiritData[8], 0xFF, sizeof(NesPPUSpiritInfoType));
    }


    /* 显示当前 pointNowY 行精灵 */
    vNesPPUDrawSpiritLine(st_typeNesPPUInfo.pointNowY, typeSpiritData);


    return 0;
}

void vNesPPUExec(void)
{
    int32_t iTimeMsDeltal;

    /* 高于240行的不显示 */
    if(st_typeNesPPUInfo.pointNowY < 240)
    {
        cNesPPULineScanf();
    }
    /* V break */
    else if(st_typeNesPPUInfo.pointNowY < 261)
    {
        if(st_typeNesPPUInfo.pointNowY == 241)
        {
            /* set VBlank flag， CPU开始更新显存 */
            st_typeNesPPUInfo.registers.PPUSTATUS |= 0x80;

            /* 判断是否触发中断 */
            if(st_typeNesPPUInfo.registers.PPUCTRL & 0x80)
            {
                g_typeNesInfo.ptypeCPUInfo->NMIFlag = TRUE;
            }
        }
    }
    /* 261行 */
    else
    {
        st_typeNesPPUInfo.v = st_typeNesPPUInfo.t;
        st_typeNesPPUInfo.pointNowY = -1;

        /* clear VBlank flag， CPU结束更新显存 */
        st_typeNesPPUInfo.registers.PPUSTATUS &= ~0x80;
        /* 清除精灵0击中标志 */
        st_typeNesPPUInfo.registers.PPUSTATUS &= ~0x40;
        /* 清除精灵溢出标志 */
        st_typeNesPPUInfo.registers.PPUSTATUS &= ~0x20;

        ++st_typeNesPPUInfo.frameCnt;

        /* 计算已经超时多少us，并延时对应的时间，以稳定到60Hz刷新 */
        iTimeMsDeltal = (st_typeNesPPUInfo.frameCnt * (1000000.0 / 50.0)) - (lNesTimeGet() - g_typeNesInfo.timeStart);

        iTimeMsDeltal /= 1000;
        vNesDelayMs(iTimeMsDeltal);
    }

    ++st_typeNesPPUInfo.pointNowY;

    /* 刷新一行，需要341个时钟 */
    st_typeNesPPUInfo.clockCnt += 341;
}

NesPPUInofType *ptyeNesPPUInfoGet(void)
{
    return &st_typeNesPPUInfo;
}
